package com.ttg.security;

//~--- non-JDK imports --------------------------------------------------------

import com.ttg.contants.TestContants;
import com.ttg.utils.Base64;
import com.ttg.utils.Bytes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

//~--- JDK imports ------------------------------------------------------------

//~--- classes ----------------------------------------------------------------

/**
 * Class RSA
 * Description
 * Create 2015-12-08 11:06:28
 * @author Ardy
 */
public class RSA {

    /**
     * Field logger
     * Description
     */
    private static final Logger logger = LoggerFactory.getLogger(RSA.class);

    /**
     * Field KEY_SIZE
     * Description
     */
    private static final int KEY_SIZE = 2048;    // RSA key 是多少位的

    /**
     * Field BLOCK_SIZE
     * Description
     */
    private static final int BLOCK_SIZE = 245;    // 一次RSA加密操作所允许的最大长度

    // 这个值与 KEY_SIZE 已经padding方法有关。因为 1024的key的输出是128，2048key输出是256字节
    // 可能11个字节用于保存padding信息了，所以最多可用的就只有245字节了。

    /**
     * Field OUTPUT_BLOCK_SIZE
     * Description
     */
    private static final int OUTPUT_BLOCK_SIZE = 256;

    /**
     * Field SIGN_ALGORITHMS
     * Description
     */
    public static final String SIGN_ALGORITHMS = "SHA1WithRSA";

    /**
     * RSA私钥签名
     * @param content 待签名数据
     * @param privateKey 商户私钥
     * @param input_charset 编码格式
     * @return 签名值
     */
    public static String signByPri(String content, String privateKey, String input_charset) {
        try {
            PKCS8EncodedKeySpec     priPKCS8  = new PKCS8EncodedKeySpec(Base64.decode(privateKey));
            KeyFactory              keyf      = KeyFactory.getInstance("RSA");
            PrivateKey              priKey    = keyf.generatePrivate(priPKCS8);
            java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);

            signature.initSign(priKey);
            signature.update(content.getBytes(input_charset));

            byte[] signed = signature.sign();

            return Base64.encode(signed);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * Method signByPub
     * Description 说明：
     *
     * @param content 说明：
     * @param publicKey 说明：
     * @param inputCharset 说明：
     *
     * @return 返回值说明：
     */
    public static String signByPub(String content, String publicKey, String inputCharset) {
        try {
            PublicKey pubKey = getPublicKey(publicKey);

            // 对数据加密
            Cipher cipher = Cipher.getInstance("RSA");

            cipher.init(Cipher.ENCRYPT_MODE, pubKey);

            return Bytes.bytes2base64(cipher.doFinal(Bytes.base642bytes(content, inputCharset)));
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * Method verifyByPub
     * Description 公钥验证签名
     * CreateDate 2015-12-16 11:22:03
     *
     * @param content String 待签名数据
     * @param sign String 签名值
     * @param public_key String 公钥
     * @param input_charset String 编码格式
     *
     * @return boolean
     */
    public static boolean verifyByPub(String content, String sign, String public_key, String input_charset) {
        try {
            KeyFactory              keyFactory = KeyFactory.getInstance("RSA");
            byte[]                  encodedKey = Base64.decode(public_key);
            PublicKey               pubKey     = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
            java.security.Signature signature  = java.security.Signature.getInstance(SIGN_ALGORITHMS);

            signature.initVerify(pubKey);
            signature.update(content.getBytes(input_charset));

            byte[]  aa      = Base64.decode(sign);
            boolean bverify = signature.verify(aa);

            return bverify;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }

    /**
     * 解密
     * @param content 密文
     * @param private_key 商户私钥
     * @param input_charset 编码格式
     * @return 解密后的字符串
     *
     * @throws Exception
     */
    public static String decrypt(String content, String private_key, String input_charset) throws Exception {
        PrivateKey prikey = getPrivateKey(private_key);
        Cipher     cipher = Cipher.getInstance("RSA");

        // 对数据解密
        cipher.init(Cipher.DECRYPT_MODE, prikey);

        InputStream           ins    = new ByteArrayInputStream(Base64.decode(content));
        ByteArrayOutputStream writer = new ByteArrayOutputStream();

        // rsa解密的字节大小最多是128，将需要解密的内容，按128位拆开解密
        byte[] buf = new byte[128];
        int    bufl;

        while (( bufl = ins.read(buf) ) != -1) {
            byte[] block = null;

            if (buf.length == bufl) {
                block = buf;
            } else {
                block = new byte[bufl];

                for (int i = 0 ; i < bufl ; i++) {
                    block[i] = buf[i];
                }
            }

            writer.write(cipher.doFinal(block));
        }

        return new String(writer.toByteArray(), input_charset);
    }

    /**
     * 得到私钥
     * @param key 密钥字符串（经过base64编码）
     *
     * @return PrivateKey
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String key) throws Exception {
        byte[] keyBytes;

        keyBytes = Base64.decode(key);

        PKCS8EncodedKeySpec keySpec    = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory          keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey          privateKey = keyFactory.generatePrivate(keySpec);

        return privateKey;
    }

    /**
     * Method getPublicKey
     * Description
     * CreateDate 2015-12-08 11:06:28
     *
     * @param key String
     *
     * @return PublicKey
     *
     * @throws Exception
     */
    public static PublicKey getPublicKey(String key) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        byte[]     encodedKey = Base64.decode(key);
        PublicKey  pubKey     = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));

        return pubKey;
    }

    /**
     * Method main
     * Description
     * CreateDate 2015-12-16 11:22:03
     *
     * @param args String[]
     */
    public static void main(String[] args) {
        String aa = null;

        try {
            aa = RSA.decrypt(
                "WB8Vyp0VkcEq+kcoMOV/z52Wa+6IJkTcKLRt1lDGGLRSRePvwRzDnkTPBwsTez3G4tBm1AgbaqHF81N35z3ntRuP+BHhH0QszEAwBs2mNRmktYBYwVPtzamT545C8r3YH1SNE2Z2UCtLJrrByU0W76FdQjz06c8ZiYJUAuu9qBMpfSlsAoX7flqAL50CNIBSsfBGHNfXRM2P4gOrdczX4e7aLUtKOoaCTwBsReElctStPSgsBJYMo63lwRQ/C0MPBCwUtlpqPcE/+dFobj/EYMFpFFuLFh9YkwMFZFzINWFOu1Y8rutVMydlOZVThQ667NklQPwYMO+NwXfWQpbqEA==", TestContants.PRIVATE_KEY, TestContants.CHARSET_UTF8);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.printf("args = [%s]%n", aa);
    }
}


//~ Formatted by Jindent --- http://www.jindent.com
